/**
 * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights
 * reserved. <a>http://www.cqcis.com</a>
 */
package com.cqcis.uip.base.framework.adaptor;

import java.util.ArrayList;
import java.util.List;

import javax.sql.DataSource;

import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.PreparedStatementCreatorFactory;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterUtils;
import org.springframework.jdbc.core.namedparam.ParsedSql;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;

import com.cqcis.uip.base.framework.utils.StringUtil;

/**
 * 命名参数解析支持
 * 
 * @author huangmy
 * @date   2008-7-29
 */
public class NamedParameterJdbcTemplateAdaptor extends NamedParameterJdbcTemplate {    
	public NamedParameterJdbcTemplateAdaptor(DataSource dataSource) {
		super(dataSource);		
	}
	
	public NamedParameterJdbcTemplateAdaptor(JdbcOperations classicJdbcTemplate) {
		super(classicJdbcTemplate);		
	}
    
	/**
	 * 重新覆盖解析SQL参数方法,如果符号':'前面无'(',',','=','['等标识时,参数视为无效.
	 * 只支持一个无效参数情况.
	 */
	@Override
	public PreparedStatementCreator getPreparedStatementCreator(String sql, SqlParameterSource paramSource) {
		String originalSql = sql;
		
		List<SqlSplits> splits = new ArrayList<SqlSplits>();
		SqlSplits sqlSplit = createSqlSplit(originalSql);
		splits.add(sqlSplit);
		while (StringUtil.hasText(sqlSplit.prefixSql)) {
			sqlSplit = createSqlSplit(sqlSplit.sql);
			splits.add(sqlSplit);
		}
		
		String sqlToUse = ""; // TO USE
		List<Object> params = new ArrayList<Object>();
		List<Integer> paramTypes = new ArrayList<Integer>();
		
		for (SqlSplits split : splits) {
			boolean last = false;
			String prefixSql = split.prefixSql;
			if (!StringUtil.hasText(prefixSql)) {
				prefixSql = split.sql;
				last = true;
			}
			
			ParsedSql parsedSql = getParsedSql(prefixSql);
			String afterSql = NamedParameterUtils.substituteNamedParameters(parsedSql, paramSource);					
			Object[] paramsNow = NamedParameterUtils.buildValueArray(parsedSql, paramSource, null);
			if (paramsNow != null) {
				for (Object param : paramsNow) {
					params.add(param);
				}
			}
			int[] paramTypesNow = NamedParameterUtils.buildSqlTypeArray(parsedSql, paramSource);
			if (paramTypesNow != null) {
				for (int paramType : paramTypesNow) {
					paramTypes.add(Integer.valueOf(paramType));
				}
			}
			sqlToUse = sqlToUse + afterSql;
			if (!last) {
				sqlToUse = sqlToUse + ":";
			}
		}
		
		int[] paramTypesAr = new int[paramTypes.size()];
		int i = 0;
		for (Integer paramType : paramTypes) {
			int type = paramType;
			paramTypesAr[i ++] = type;
		}
		
		PreparedStatementCreatorFactory pscf = new PreparedStatementCreatorFactory(sqlToUse, paramTypesAr);
		PreparedStatementCreator psc = pscf.newPreparedStatementCreator(params.toArray(new Object[params.size()]));
		
		return psc;
	}
	
	private SqlSplits createSqlSplit(String originalSql) {
		String sql = originalSql;
		int pos = -1;		
		int prexPos = -1;		
		while ((pos = sql.indexOf(':', pos + 1)) >= 0 
				&& isNotForward(sql, pos, '(')
				&& isNotForward(sql, pos, ',')
				&& isNotForward(sql, pos, '=')
				&& isNotForward(sql, pos, '[') ) {
			sql = sql.substring(pos + 1);
			prexPos = pos;
		}
		
		String prefixSql = (prexPos >= 0) ? originalSql.substring(0, prexPos) : null;
		prefixSql = StringUtil.hasText(prefixSql) ? prefixSql : "";
		
		return new SqlSplits(prefixSql, sql);
	}
	
	private boolean isNotForward(String sql, int pos, char c) {		
		int numPos = sql.lastIndexOf(c, pos - 1);
		if (numPos < 0) {
			return true;
		}
		
		String preSql = numPos + 1 <= pos ? sql.substring(numPos + 1, pos) : null;
		if (StringUtil.hasText(preSql)) {
			return true;
		}
		
		return false;
	}
	
	private class SqlSplits {
		private String prefixSql; // 前一SQL
		private String sql; // 后SQL
		
		SqlSplits(String prefixSql, String sql) {
			this.prefixSql = StringUtil.hasText(prefixSql) ? prefixSql : "";
			this.sql = StringUtil.hasText(sql) ? sql : "";
		}
		
		@Override
		public String toString() {
			return "{prefixSql=" + prefixSql + ", sql=" + sql + "}";
		}
	}
}
